﻿//Copyright Microsoft. All rights reserved.
//Licensed under the MIT License at http://mit-license.org/

//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.

using Newtonsoft.Json.Linq;
using System;
using System.Configuration;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;


// This Windows console application is a demo for Azure Search. It outputs search query results for multiple scoring profiles.
// Before building and running this app:
// 1. update app.config with your admin key and Azure Search service URL from the Azure Search service dashboard at https://portal.azure.com
// 2. add a reference to the Newtonsoft.Json assmembly. Right-click Scoring.NET project | Manage NuGet Packages | Online | Json.NET | Install.

// This demo includes a schema for creating an index and data files for populating the index.
// Schema.json defines the index. The scoring profiles used in this demo are included in the schema.
// Data-n.json files provide the data used in this solution. No additional data is required to run this sample.

namespace Scoring.NET
{
    class Program
    {
        private const string ApiVersion = "api-version=2014-07-31-Preview";
        private static string serviceUrl = ConfigurationManager.AppSettings["serviceUrl"];
        private static string indexName = ConfigurationManager.AppSettings["indexName"];
        private static string primaryKey = ConfigurationManager.AppSettings["primaryKey"];

        static void Main(string[] args)
        {
            JObject response;

            if (!serviceUrl.EndsWith("/"))
            {
                serviceUrl += "/";
            }

            if (!serviceUrl.EndsWith("indexes/"))
            {
                serviceUrl += "indexes/";
            }

            try
            {
                // Delete the Index if it exists
                DeleteIndex(serviceUrl + indexName);

                // Create the index
                if (CreateIndex() == false)
                    return;

                // Add documents to index, loading in 100 document batches. Each data file has 100 items or less.
                if (PostDocuments("data1.json") == false)
                    return;

                if (PostDocuments("data2.json") == false)
                    return;

                if (PostDocuments("data3.json") == false)
                    return;

                // Wait 5 seconds
                Console.WriteLine("Waiting 5 seconds for data to be indexed...");
                Thread.Sleep(TimeSpan.FromSeconds(5));

                //Start the queries and output results to the console.
                //Query #1 is a search action using the term 'best', with no boosting
                //Search results are constrained to the top 10.
                //Default scoring uses all fields to calculate a score, but the search results are scoped to albumtitle, genre, artistname, rate, and lastUpdated.
                response = ExecuteRequest("search", "=best&$top=10&$select=albumTitle,genre,artistName,rating,lastUpdated");
                Console.WriteLine("Search Results for search term 'best' with no boosting: \r\n \r\n {0} \r\n", response);

                //Query #2 searches the Index for the term 'best' where genre is boosted via a scoring profile 'boostGenre.'
                //Notice how search results are the same as unboosted. This is becuase the search term, 'best', does not exist in the genre field
                response = ExecuteRequest("search", "=best&$top=10&$select=albumTitle,genre,artistName,rating,lastUpdated&ScoringProfile=boostGenre");
                Console.WriteLine("Search Results for search term 'best' boosted for genre: \r\n \r\n {0} \r\n", response);

                //Query #3 searches the Index for the term 'best' applying boosting from Rating and Most Recent items
                //Notice that the search scores result in an alternative rank order
                response = ExecuteRequest("search", "=best&$top=10&$select=albumTitle,genre,artistName,rating,lastUpdated&ScoringProfile=newAndHighlyRated");
                Console.WriteLine("Search Results for search term 'best' boosted by new and highly rated : \r\n \r\n {0} \r\n", response);

                //Query #4 searches the Index for the term 'best' and is boosted by margin. 
                //Margin is non-searchable and cannot be returned in the field list, but it is used in the search score calculation
                //Notice the significant change in search scores. This is due to the wide range in values that exist in the fictional 'margin' field
                response = ExecuteRequest("search", "=best&$top=10&$select=albumTitle,genre,artistName&ScoringProfile=boostMargin");
                Console.WriteLine("Search Results for search term 'best' boosted by margin : \r\n \r\n {0} \r\n", response);
            }
            catch (Exception e)
            {
                Console.WriteLine("Unhandled exception caught: {0}", e.Message);
                Console.WriteLine("\nDid you remember to paste your service URL and API key into App.config?\n");
            }

            Console.Write("Complete. Press <enter> to continue: ");
            var name = Console.ReadLine();

        }

        static bool CreateIndex()
        {
            // Add some data to the newly created index
            Uri requestUri = new Uri(serviceUrl + indexName + "?" + ApiVersion);

            // Load the json containing the schema from an external file
            string json = File.ReadAllText("schema.json");

            using (HttpClient client = new HttpClient())
            {
                // Create the index
                client.DefaultRequestHeaders.Add("api-key", primaryKey);
                HttpResponseMessage response = client.PutAsync(requestUri,        // To create index use PUT
                    new StringContent(json, Encoding.UTF8, "application/json")).Result;

                if (response.StatusCode == HttpStatusCode.Created)   // For Posts we want to know response had no content
                {
                    Console.WriteLine("Index created. \r\n");
                    return true;
                }

                Console.WriteLine("Index creation failed: {0} {1} \r\n", (int)response.StatusCode, response.Content.ReadAsStringAsync().Result.ToString());
                return false;

            }
        }

        static bool PostDocuments(string fileName)
        {
            // Add some documents to the newly created index
            Uri requestUri = new Uri(serviceUrl + indexName + "/docs/index?" + ApiVersion);

            // Load the json containing the data from an external file
            string json = File.ReadAllText(fileName);

            using (HttpClient client = new HttpClient())
            {
                // Create the index
                client.DefaultRequestHeaders.Add("api-key", primaryKey);
                HttpResponseMessage response = client.PostAsync(requestUri,       // To add data use POST
                    new StringContent(json, Encoding.UTF8, "application/json")).Result;

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    Console.WriteLine("Documents posted from file {0}. \r\n", fileName);
                    return true;
                }

                Console.WriteLine("Documents failed to upload: {0} {1} \r\n", (int)response.StatusCode, response.Content.ReadAsStringAsync().Result.ToString());
                return false;

            }
        }

        static JObject ExecuteRequest(string action, string query = "")
        {
            // original:  string url = serviceUrl + indexName + "/" + action + "?" + ApiVersion; 
            string url = serviceUrl + indexName + "/docs?" + action ;
            if (!String.IsNullOrEmpty(query))
            {
                url += query + "&" + ApiVersion;
            }

            string response = ExecuteGetRequest(url);
            return JObject.Parse(response);

        }

        static string ExecuteGetRequest(string requestUri)
        {
            //This will execute a get request and return the response
            using (HttpClient client = new HttpClient())
            {
                client.DefaultRequestHeaders.Add("api-key", primaryKey);
                HttpResponseMessage response = client.GetAsync(requestUri).Result;        // Searches are done over port 80 using Get
                return response.Content.ReadAsStringAsync().Result;
            }

        }

        static bool DeleteIndex(string requestUri)
        {
            //This will execute a delete request
            using (HttpClient client = new HttpClient())
            {
                client.DefaultRequestHeaders.Add("api-key", primaryKey);
                HttpResponseMessage response = client.DeleteAsync(requestUri + "?" + ApiVersion).Result;
                if (response.StatusCode == HttpStatusCode.NoContent)
                {
                    Console.WriteLine("Index Deleted. \r\n");
                    return true;
                }
                else if (response.StatusCode == HttpStatusCode.NotFound)
                {
                    Console.WriteLine("Could not find existing index, continuing... \r\n");
                    return true;
                }

                Console.WriteLine("Index Deletion Failed: : {0} {1} \r\n", (int)response.StatusCode, response.Content.ReadAsStringAsync().Result.ToString());
                return false;
            }

        }

    }
}
